package top.codedance.iotp.server.net.handle;

import cd.io.netty.handler.codec.http.FullHttpRequest;
import top.codedance.iotp.server.ServerApplication;
import top.codedance.iotp.server.net.handle.subhandle.TextWsMsgHandler;
import cd.io.netty.buffer.Unpooled;
import cd.io.netty.channel.Channel;
import cd.io.netty.channel.ChannelHandlerContext;
import cd.io.netty.channel.SimpleChannelInboundHandler;
import cd.io.netty.channel.socket.SocketChannel;
import cd.io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import cd.io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.apache.log4j.Logger;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class WsChannelHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    private static final Logger logger = ServerApplication.getLogger(WsChannelHandler.class);

    public static volatile ConcurrentMap<String, Channel> channelMap = new ConcurrentHashMap<>();
    private SocketChannel channel;
    private WsMsgHandler handler;
    private boolean isClosed = true;

    public boolean isClosed() {
        return isClosed;
    }

    public WsChannelHandler(SocketChannel channel) { this.channel = channel;}

    /**
     * 生成客户端的唯一标识
     * @param channelHandlerContext
     * @return
     */
    public static String getClientId(ChannelHandlerContext channelHandlerContext){
        InetSocketAddress ipSocket = (InetSocketAddress) channelHandlerContext.channel().remoteAddress();
        String address = ipSocket.getAddress().getHostAddress();
        return address + "-" + channelHandlerContext.channel().id();
    }

    protected void messageReceived(final ChannelHandlerContext channelHandlerContext, final TextWebSocketFrame tf) throws Exception {
        if(ServerApplication.isSafyMode){
            channelHandlerContext.close();
            return;
        }
        final String clientId = getClientId(channelHandlerContext);
        final byte[] bytes = tf.text().getBytes(StandardCharsets.UTF_8);
        ServerApplication.workerPool.submit(() -> {
            try {
                handler = new TextWsMsgHandler();
                FullHttpRequest request = WsChannelAuthHandler.wsReqests.get(clientId);
                handler.channelRead(WsChannelHandler.this, clientId, channelHandlerContext, request, bytes);
            }catch (Exception e){
                logger.debug(e.getMessage());
            }
        });
        handlerAdded(channelHandlerContext);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ServerApplication.syslogInfo("client connect success.");
        String clientId = getClientId(ctx);
        logger.debug("client id: " + clientId);
        synchronized (WsChannelHandler.class) {
            if (!channelMap.containsKey(clientId)) {
                channelMap.put(clientId, ctx.channel());
            }
        }
        isClosed = false;
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ServerApplication.syslogInfo("WsClient is closed.");
        String clientId = getClientId(ctx);
        synchronized (WsChannelHandler.class) {
            channelMap.remove(clientId);
        }
        synchronized (WsChannelAuthHandler.class) {
            WsChannelAuthHandler.wsReqests.remove(clientId);
        }
        isClosed = true;
    }

    /**
     * 发送二进制数据
     * @param message
     */
    public void sendByteArrayMessage(String message){
        BinaryWebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(Unpooled.buffer().writeBytes(message.getBytes()));
        channel.writeAndFlush(binaryWebSocketFrame);
    }

    /**
     * 像客户端发送消息
     * @param cid
     * @param body
     */
    public static synchronized void wsSend(String cid, String body){
        if(channelMap.containsKey(cid)) {
            Channel channel = channelMap.get(cid);
            channel.writeAndFlush(new TextWebSocketFrame(body));
        }
    }

    public static synchronized boolean wsIsActive(String cid){
        return channelMap.containsKey(cid);
    }
}
